﻿// Copyright (c) Vitaly Kamiansky. All rights reserved. 
// See License.txt in the project root.

using Property.Objects;
using Property.Reloaders;
using System;

namespace Property
{
    /// <summary>
    /// Contains words that extend the description 
    /// of a property's reload process.
    /// </summary>
    public static class ReloaderPrototypeExtensionsCore
    {
        /// <summary>
        /// ... (if) the specified condition is met (when) ...
        /// </summary>
        /// <param name="condition">Tell me how to decide if the reload should happen.</param>
        public static IReloaderPrototype<T> Conditional<T>(this IReloaderPrototype<T> source, Func<bool> condition)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (condition == null) throw new ArgumentNullException("condition");

            return new ConditionReloaderDecoratorPrototype<T>(source, condition);
        }

        /// <summary>
        /// ... (if) the internally stored state meets the specified condition (when) ...
        /// </summary>
        /// <param name="getState">Tell me how the state should be produced 
        /// based on its previous value.</param>
        /// <param name="condition">Tell me how to use the state to decide if 
        /// the reload should happen.</param>
        public static IReloaderPrototype<T> Conditional<T, TState>(this IReloaderPrototype<T> source, Func<TState, TState> getState, 
            Func<TState, bool> condition)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (getState == null) throw new ArgumentNullException("getState");
            if (condition == null) throw new ArgumentNullException("condition");

            return new StateConditionReloaderDecoratorPrototype<T, TState>(source, getState, condition);
        }

        /// <summary>
        /// ... first time ...
        /// </summary>
        public static IReloaderPrototype<T> First<T>(this IReloaderPrototype<T> source)
        {
            return source.Conditional<T, bool>(_ => true, o => !o);
        }

        /// <summary>
        /// ... every odd time ...
        /// </summary>
        public static IReloaderPrototype<T> Odd<T>(this IReloaderPrototype<T> source)
        {
            return source.Conditional<T, bool>(o => !o, o => !o);
        }

        /// <summary>
        /// <para>... the user tries to get the property's Value.</para>
        /// <para>A new property value will be produced using the specified function.</para>
        /// </summary>
        /// <param name="getValue">Tell me how to use the current value of the property 
        /// to produce its new one.</param>
        public static IPropertyPrototype<T> Get<T>(this IReloaderPrototype<T> source, Func<T, T> getValue)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (getValue == null) throw new ArgumentNullException("getValue");

            return new GetPropertyPrototype<T>(source, getValue);
        }

        /// <summary>
        /// <para>... the user sets the property's Input.</para>
        /// <para>A new property value will be read from Input.</para>
        /// </summary>
        public static IInputPropertyPrototype<T> Input<T>(this IReloaderPrototype<T> source)
        {
            if (source == null) throw new ArgumentNullException("source");

            return new InputPropertyPrototype<T>(source);
        }

        /// <summary>
        /// <para>... the user calls the property's Go() method.</para>
        /// <para>A new property value will be produced using the specified function.</para>
        /// </summary>
        /// <param name="getValue">Tell me how to use the current value of the property 
        /// to produce its new one.</param>
        public static ICallPropertyPrototype<T> Call<T>(this IReloaderPrototype<T> source, Func<T, T> getValue)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (getValue == null) throw new ArgumentNullException("getValue");

            return new CallPropertyPrototype<T>(source, getValue);
        }

        /// <summary>
        /// <para>... the user tries to get the property's Value or calls its Go method.</para>
        /// <para>A new property value will be produced using the specified function.</para>
        /// </summary>
        /// <param name="getValue">Tell me how to use the current value of the property 
        /// to produce its new one.</param>
        public static ICallPropertyPrototype<T> CallGet<T>(this IReloaderPrototype<T> source, Func<T, T> getValue)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (getValue == null) throw new ArgumentNullException("getValue");

            return new CallGetPropertyPrototype<T>(source, getValue);
        }
    }
}
